package org.sqlproc.engine.impl;

import org.hibernate.SQLQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The entity for a dynamic input value.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
class SqlInputValue {

    /**
     * The internal slf4j logger.
     */
    final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * Enumeration for no/upper/lower case conversion. This is done on dynamic/static input values.
     * 
     */
    static enum Case {
        /**
         * No case conversion.
         */
        NONE,
        /**
         * Upper case conversion.
         */
        UPPER,
        /**
         * Lower case conversion.
         */
        LOWER
    };

    /**
     * Which conversion should be done on inputValue.
     */
    private Case caseConversion;
    /**
     * A dynamic input value.
     */
    private Object inputValue;
    /**
     * A wildcard character for SQL statement <code>like</code>. It can be added to inputValue as a prefix and/or as a
     * suffix.
     */
    private String likeChar;
    /**
     * The minimum length of inputValue to enable likeChar special treatment.
     */
    private int minLikeLength;
    /**
     * Dynamic input value type.
     */
    private SqlType type;

    /**
     * Creates a new instance of this entity. Used from inside ANTLR parser.
     * 
     * @param inputValue
     *            a dynamic input value
     * @param caseConversion
     *            which conversion should be done on inputValue
     * @param type
     *            a type of dynamic input value
     */
    SqlInputValue(Object inputValue, Case caseConversion, SqlType type) {
        this.inputValue = inputValue;
        this.caseConversion = caseConversion;
        this.type = type;
    }

    /**
     * Bind a dynamic input value to a named query parameter.
     * 
     * @param query
     *            the object-oriented representation of a Hibernate query
     * @param paramName
     *            the name of the parameter
     */
    void setQueryParam(SQLQuery query, String paramName) {
        Object o = this.inputValue;

        if (this.inputValue instanceof String) {
            if (caseConversion == Case.NONE) {
                o = processLike(this.inputValue);
            } else if (caseConversion == Case.LOWER) {
                o = this.inputValue != null ? processLike(this.inputValue).toLowerCase() : (String) null;
            } else if (caseConversion == Case.UPPER) {
                o = this.inputValue != null ? processLike(this.inputValue).toUpperCase() : (String) null;
            }
        }
        type.setParameter(query, paramName, o);
    }

    /**
     * Sets a special treatment of dynamic input value
     * 
     * @param likeChar
     *            a wildcard character
     * @param minLikeLength
     *            the minimum length of inputValue to enable likeChar special treatment
     */
    void setLike(String likeChar, Integer minLikeLength) {
        this.likeChar = likeChar;
        this.minLikeLength = (minLikeLength == null) ? 1 : minLikeLength.intValue();
    }

    /**
     * A special treatment of dynamic input value for SQL statement <code>like</code>.
     * 
     * @param val
     *            the dynamic input value
     * @return a dynamic input value with a likeChar as a prefix and/or suffix
     */
    private String processLike(Object val) {
        String param = (String) val;
        if (likeChar != null && param != null) {
            param = param.trim();
            int length = param.length();
            boolean startsWith = param.startsWith(this.likeChar);
            boolean endsWith = param.endsWith(this.likeChar);
            if (startsWith && endsWith)
                return param;
            else if (startsWith) {
                if (length >= minLikeLength + 1)
                    return param + likeChar;
                else
                    return param;
            } else if (endsWith) {
                if (length >= minLikeLength + 1)
                    return likeChar + param;
                else
                    return param;
            } else {
                if (length >= minLikeLength)
                    return likeChar + param + likeChar;
                else
                    return param;
            }
        }
        return param;
    }

    /**
     * For debug purposes.
     * 
     * @return a String representation for a debug output
     */
    public String toString() {
        StringBuilder sb = new StringBuilder("SqlInputValue:");
        sb.append(" caseConversion=").append(caseConversion);
        sb.append(" value='").append(inputValue).append("'");

        return sb.toString();
    }
}
